﻿using System;
using System.Threading;


namespace ALProtocol.Tcp
{
    /// <summary>
    /// 表示TCP数据写入。
    /// </summary>
    public class TcpWriting
    {
        /// <summary>
        /// 数据缓存。
        /// </summary>
        private byte[] m_buffer;
        /// <summary>
        /// 数据偏移。
        /// </summary>
        private int m_offset;
        /// <summary>
        /// 数据长度。
        /// </summary>
        private int m_length;
        /// <summary>
        /// TCP会话。
        /// </summary>
        private TcpSession m_session;

        /// <summary>
        /// 开始通知。
        /// </summary>
        private Action m_benginWrite;
        /// <summary>
        /// 结束通知。
        /// </summary>
        private Action<int> m_endWrite;
        /// <summary>
        /// 开始通知事件。
        /// </summary>
        public event Action BeginWrite
        {
            add { m_benginWrite += value; }
            remove { m_benginWrite -= value; }
        }
        /// <summary>
        /// 结束通知事件。
        /// </summary>
        public event Action<int> EndWrite
        {
            add { m_endWrite += value; }
            remove { m_endWrite -= value; }
        }

        /// <summary>
        /// 实例化一个新的TCP数据写入。
        /// </summary>
        public TcpWriting()
        {
            m_buffer = new byte[0];
            m_offset = 0;
            m_length = 0;
            m_session = null;
        }
        /// <summary>
        /// 实例化一个新的TCP数据写入。
        /// </summary>
        /// <param name="length"></param>
        public TcpWriting(int length)
        {
            m_buffer = new byte[length];
            m_offset = 0;
            m_length = length;
            m_session = null;
        }
        /// <summary>
        /// 实例化一个新的TCP数据写入。
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        public TcpWriting(byte[] buffer, int offset, int length)
        {
            m_buffer = buffer;
            m_offset = offset;
            m_length = length;
            m_session = null;
        }
        /// <summary>
        /// 实例化一个新的TCP数据写入。
        /// </summary>
        /// <param name="buffer"></param>
        public TcpWriting(byte[] buffer)
        {
            m_buffer = buffer;
            m_offset = 0;
            m_length = buffer.Length;
            m_session = null;
        }

        /// <summary>
        /// 获取数据缓存。
        /// </summary>
        public byte[] Buffer
        {
            get { return m_buffer; }
        }
        /// <summary>
        /// 获取数据偏移。
        /// </summary>
        public int Offset
        {
            get { return m_offset; }
        }
        /// <summary>
        /// 获取数据长度。
        /// </summary>
        public int Length
        {
            get { return m_length; }
        }

        /// <summary>
        /// 获取写入状态，如果正在写入，则返回True，否则返回0。
        /// </summary>
        public bool Status
        {
            get { return m_session != null; }
        }

        /// <summary>
        /// 设置要发送的数据。
        /// 如果设置成功，则返回True，如果当前正在发送，则返回False。
        /// </summary>
        /// <param name="buffer">数据缓存。</param>
        /// <param name="offset">数据偏移。</param>
        /// <param name="length">数据长度。</param>
        /// <returns>如果设置成功，则返回True，如果当前正在发送，则返回False。</returns>
        public bool Assign(byte[] buffer, int offset, int length)
        {
            if (m_session != null)
                return false;
            m_buffer = buffer;
            m_offset = offset;
            m_length = length;
            return true;
        }
        /// <summary>
        /// 设置要发送的数据。
        /// 如果设置成功，则返回True，如果当前正在发送，则返回False。
        /// </summary>
        /// <param name="offset">数据偏移。</param>
        /// <param name="length">数据长度。</param>
        /// <returns>如果设置成功，则返回True，如果当前正在发送，则返回False。</returns>
        public bool Assign(int offset, int length)
        {
            if (m_session != null)
                return false;
            m_offset = offset;
            m_length = length;
            return true;
        }

        /// <summary>
        /// 当开始写入时调用此方法。
        /// </summary>
        protected virtual void OnBegin()
        {
            if (m_benginWrite != null)
            {
                m_benginWrite();
            }
        }
        /// <summary>
        /// 当结束写入时调用此方法。
        /// </summary>
        /// <param name="length"></param>
        protected virtual void OnEnd(int length)
        {
            if (m_endWrite != null)
            {
                m_endWrite(length);
            }
        }

        /// <summary>
        /// 当开始写入时调用此方法。
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        internal bool Retain(TcpSession session)
        {
            if (Interlocked.CompareExchange<TcpSession>(ref m_session, session, null) == null)
            {
                this.OnBegin();
                return true;
            }
            return false;
        }
        /// <summary>
        /// 当写入完成后调用此方法。
        /// </summary>
        /// <param name="length">写入的数据量。</param>
        internal void Fulfil(int length)
        {
            Interlocked.Exchange<TcpSession>(ref m_session, null);
            this.OnEnd(length);
        }
    }
}
